//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler execution subsystem
// FILE:            XortMachine.cpp - CPU + stack state for JIT code generator
// AUTHOR:          Vladimir Gumenuk
// DATE:            27-Oct-2014
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xort.h"
#include "XortMachine.h"

XortMachine::XortMachine()
{
    m_dwStackBase = 0;
    m_dwLocalStorageStartIdx = 0;
    m_dwNumLocals = 0;
}

XortMachine::~XortMachine()
{

}

void XortMachine::BuildArgumentsLayout( Symbol *pFunc ) // fill registers and stack with arguments before function call
{
    m_Registers.clearAll();
    m_Registers.setRegValue( RBP, t_u8 ); // initially, mark them as used
    m_Registers.setRegValue( RSP, t_u8 );
    m_Stack.clear();

    int iIdx = 0;

    XCELLSARRAY stkTemp;

    SYMBOL_LIST::iterator theIter = pFunc->m_listMembers.end();
    theIter--; // skip local scope marker
    symbolref pArg = (theIter == NULL) ? NULL : (Symbol*)theIter;
    while( pArg )
    {
        switch( iIdx )
        {
        case 0: m_Registers.setRegValue( RCX, x_clamp_type( pArg->m_dwType ) ); break; // TODO: for class members, here should be this pointer...
        case 1: m_Registers.setRegValue( RDX, x_clamp_type( pArg->m_dwType ) ); break;
        case 2: m_Registers.setRegValue( R8, x_clamp_type( pArg->m_dwType ) ); break;
        case 3: m_Registers.setRegValue( R9, x_clamp_type( pArg->m_dwType ) ); break;
        case 4: m_Registers.setRegValue( R10, x_clamp_type( pArg->m_dwType ) ); break;
        case 5: m_Registers.setRegValue( R11, x_clamp_type( pArg->m_dwType ) ); break;
        case 6: m_Registers.setRegValue( R12, x_clamp_type( pArg->m_dwType ) ); break;
        case 7: m_Registers.setRegValue( R13, x_clamp_type( pArg->m_dwType ) ); break;
        case 8: m_Registers.setRegValue( R14, x_clamp_type( pArg->m_dwType ) ); break;
        case 9: m_Registers.setRegValue( R15, x_clamp_type( pArg->m_dwType ) ); break;
        default: {
            XortElement v; v.bType = x_clamp_type( pArg->m_dwType ); v.bAccess = usr_VarR;
            stkTemp.push( v );
        }
            break;
        }
        iIdx++;
        theIter--;
        pArg = theIter == NULL ? NULL : (Symbol*)theIter;
    }

    while( stkTemp.count() ) // reverse arguments from xvm passcall convention
    {
        m_Stack.push( stkTemp.pop() );
    }

    XortElement v; v.bType = t_u8; v.bAccess = sys_Frame;
    m_dwStackBase = m_Stack.push( v ); // ret addr
    m_Stack.push( v ); // saved rbp
}

void XortMachine::ReserveLocalStorage( int iNumLocals )
{
    XortElement v;
    m_dwLocalStorageStartIdx = m_Stack.push( v );
    for( int i = 1; i < iNumLocals; i++ )
    {
        m_Stack.push( v );
    }
    m_dwNumLocals = (dword)iNumLocals;
}

void XortMachine::RemoveLocalStorage( int iNumLocals )
{
    for( int i = 0; i < iNumLocals; i++ )
    {
        m_Stack.pop();
    }
    m_dwLocalStorageStartIdx = 0;
}

VarStorage XortMachine::findStorage( const XortElement& val )
{
    VarStorage st;

    Register64 rg = m_Registers.findFreeReg();
    if( rg == NUM_REGS )
    {
        dword dwIdx = m_Stack.push( val );
        st.iType = VarStorage_STACK;
        st.iIndex = (int)(dwIdx - m_dwStackBase);
    }
    else
    {
        m_Registers.setRegValue( rg, val.bType );
        st.iType = VarStorage_REG;
        st.iIndex = (int)rg;
    }

    return st;
}

void XortMachine::clearStorage( const VarStorage& st )
{
    if( st.iType == VarStorage_REG )
    {
        m_Registers.setRegValue( (Register64)st.iIndex, t_v );
    }
    else if( st.iType == VarStorage_STACK )
    {
        if( st.iIndex == m_Stack.count()-1 )
        {
            m_Stack.pop();
        } else {
            XortElement& v = m_Stack.get( st.iIndex );
            v.bType = t_v;
        }
    }
}

VarStorage XortMachine::findLocalStorage( const XortElement& val ) // return empty slot from local variables
{
    VarStorage st;
    
    dword dwLocalStorageEnd = getLocalStorageEnd();
    for( dword i = m_dwLocalStorageStartIdx; i < dwLocalStorageEnd; i++ )
    {
        XortElement& v = m_Stack.get( i );
        if( v.bType == t_v )
        {
            v.bType = val.bType;
            st.iType = VarStorage_STACK;
            st.iIndex = (int)i;
            break;
        }
    }

    return st;
}

void XortMachine::clearLocalStorage( VarStorage st )
{
    if( st.iType == VarStorage_STACK )
    {
        XortElement& v = m_Stack.get( st.iIndex );
        v.bType = t_v;
    }
}

XortElement XortMachine::getFromStorage( const VarStorage& st )
{
    XortElement v;

    if( st.iType == VarStorage_REG )
    {
        v = m_Registers.getRegValue( (Register64)st.iIndex );
    }
    else if( st.iType == VarStorage_STACK )
    {
        if( st.iIndex < (int)m_Stack.count() )
        {
            v = m_Stack.get( st.iIndex );
        }
    }

    return v;
}

int XortMachine::getStackBase() const
{
    return (int)m_dwStackBase;
}

int XortMachine::getStackTop() const
{
    return (int)m_Stack.count() - 1;
}

int XortMachine::getLocalStorageEnd() const
{
    return (int)( m_dwLocalStorageStartIdx + m_dwNumLocals );
}

XortElement XortMachine::getRegValue( Register64 rg )
{
    XortElement v;
    v.bType = m_Registers.getRegValue( rg );
    return v;
}

XortElement XortMachine::getFromStack( int iIdx )
{
    XortElement v;
    v = m_Stack.get( m_dwLocalStorageStartIdx + iIdx ); // TODO: make index valid!!!
    return v;
}

void XortMachine::Push( const XortElement& val ) // push some value to hardware stack
{
    m_Stack.push( val );
}

XortElement XortMachine::Pop() // pop some value from hardware stack
{
    XortElement v = m_Stack.pop();
    return v;
}

bool XortMachine::testCanAccessStk( int iIdx )
{
    return ( iIdx < (int)m_Stack.count() ) ? true : false;
}